#include "TcpSession.h"

namespace mp_net
{
	TcpSession::TcpSession(const uint maxSockets) :	
		Threaded(), RecursiveLockingMutex(), 
		m_ioService(), m_socketCount(0), m_maxSockets(maxSockets),
		m_socketIndex(0)
	{

		m_socket = new tcp::socket*[maxSockets];
		m_connectedSessions = new byte[maxSockets];
		m_outPacket = new TcpPacket*[maxSockets];
		m_inPacket = new TcpPacket*[maxSockets];

		for(uint i = 0; i < maxSockets; i++)
		{
			m_socket[i] = NULL;
			m_connectedSessions[i] =
				MPN_TCP_SESSION_NO_ID;
			m_outPacket[i] = NULL;
			m_inPacket[i] = NULL;
		}
	}

	TcpSession::~TcpSession()
	{ 
		//Lock data
		if (!TryLock())
			Pause(1);
		//Stop thread
		StopThread(true);
		//Delete sockets used
		for(int i = 0; i < m_socketCount; i++)
		{
			delete m_socket[i];
			delete m_outPacket[i];
			delete m_inPacket[i];
		}
		delete [] m_socket;
		delete [] m_outPacket;
		delete [] m_inPacket;
		//Unlock
		Unlock();
	}
	
	//Session Management -----------------------

	void TcpSession::SetIoService(spIoService ioService)
	{
		m_ioService = ioService;
		UpdateSockets();
	}
	

	//Socket Management -----------------------

	bool TcpSession::AddSocket(tcp::socket* socket)
	{
		if ( m_socketCount >= m_maxSockets )
			return false;
		m_socket[m_socketCount++] = std::move(socket);
		return true;
	}

	void TcpSession::UpdateSockets()
	{
		for(uint i = 0 ; i < m_socketCount; i++)
		{
			if ( m_socket[i] != NULL )
			{
				delete m_socket[i];
				m_socket[i] = new tcp::socket(*m_ioService);
			}
		}
	}

	void TcpSession::RemoveSocket(tcp::socket* socket)
	{

	}

	//Threading -----------------------

	void TcpSession::ThreadInitialization()
	{
		
	}

	void TcpSession::ThreadUpdate()
	{
		AsyncSend();
		AsyncReceive();

		//At the end of the session operation, we
		// run any pending operations.
		boost::system::error_code error;
		m_ioService->poll(error);
		if (error)
		{
			throw "OH NO!";
		}
	}

	void TcpSession::ThreadShutdown()
	{

	}

	//Socket Threading -----------------------
	
	void TcpSession::AsyncSend()
	{
		//for each active socket
		for(m_socketIndex = 0; 
			m_socketIndex < m_socketCount; 
			m_socketIndex++)
		{
			//If we are ready to send
			if (m_outPacket[m_socketIndex]->IsUpdated())
			{
				std::cout << "Sending packet to [" << m_socketIndex << "]\n";
				//Locks the packet
				while(!m_outPacket[m_socketIndex]->TryLock()){Pause(MPN_TCP_MTX_LOCK_DELAY);}
				try
				{
					char* msg;
					uint msgLength;
					m_outPacket[m_socketIndex]->GetBuiltPacket(msg, msgLength);

					//Send message here
					boost::asio::async_write(*m_socket[m_socketIndex],
						boost::asio::buffer(msg, msgLength),
						boost::bind(
							&TcpSession::AsyncSendProc, 
							this,
							boost::asio::placeholders::error,
							boost::asio::placeholders::bytes_transferred,
							m_socketIndex));
				}
				catch (boost::system::error_code const& error)
				{
					m_outPacket[m_socketIndex]->Unlock();
					throw "TcpSession::AsyncSend Error!";
				}
			}
		}
	}

	void TcpSession::AsyncSendProc(
		const boost::system::error_code& error,
        std::size_t nBytesTransferred, byte index)
	{
		//Todo - check sent size with size on hand
		m_outPacket[index]->Updated(false);

		m_outPacket[index]->Unlock();	//Unlock on completion or error
		if (!error)
		{
			std::cout << "[TcpSession] Error: " << error.message() << "\n";
		}
	}

	void TcpSession::AsyncReceive()
	{
		//for each active socket
		for(m_socketIndex = 0; 
			m_socketIndex < m_socketCount; 
			m_socketIndex++)
		{
			if (!m_outPacket[m_socketIndex]->IsUpdated())
			{
				std::cout << "Reading packet from [" << m_socketIndex << "]\n";
				//Locks the packet
				while(!m_inPacket[m_socketIndex]->TryLock()){Pause(MPN_TCP_MTX_LOCK_DELAY);}
				try
				{
					//Receive message here
					char* msg;
					uint msgLength;
					boost::asio::async_read(
						*m_socket[m_socketIndex],
						boost::asio::buffer(msg, msgLength),
						boost::bind(
							&TcpSession::AsyncReceiveProc, 
							this,
							boost::asio::placeholders::error,
							m_socketIndex));

				}
				catch (boost::system::error_code const& error)
				{
					m_inPacket[m_socketIndex]->Unlock();
					throw "TcpSession::AsyncReceive Error!";
				}
			}
		}
	}

	void TcpSession::AsyncReceiveProc(const boost::system::error_code& error,
		byte index)
	{
		m_inPacket[index]->Unlock();	//Unlock on completion or error
		if (!error)
		{

		}
	}



}